﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

namespace Central.Linq {

	/// <summary>
	/// Alex: коллекция полей для запроса
	/// </summary>
	public class FieldManager {
		#region конструктор

		/// <summary>
		/// Создает экземпляр коллекции полей запроса
		/// </summary>
		public FieldManager() {
			this.fieldDefinitions = new Collection<FieldDefinition>();
		}

		/// <summary>
		/// Создает экземпляр коллекции полей запроса c предустановленной коллекцией полей
		/// </summary>
		public FieldManager(Collection<FieldDefinition> fieldDefinitionCollection) {
			this.fieldDefinitions = fieldDefinitionCollection;
		}

		#endregion конструктор

		#region [+] cвойства

		private Collection<FieldDefinition> fieldDefinitions;

		/// <summary>
		/// Коллекция полей
		/// </summary>
		public IEnumerable<FieldDefinition> FieldDefinitions { get { return fieldDefinitions; } }

		private SelectPredicate predicateDefinition;

		/// <summary>
		/// Входной предикат выборки
		/// </summary>
		public SelectPredicate PredicateDefinition {
			get {
				return this.predicateDefinition;
			}
			set {
				this.predicateDefinition = value;
			}
		}

		/// <summary>
		/// Выходной предикат выборки по коллекции полей
		/// </summary>
		public SelectPredicate Predicate {
			get {
				return this.GeneratePredicate();
			}
		}

		/// <summary>
		/// Возвращаемое количество полей выборки
		/// </summary>
		public int Count {
			get {
				return
						(this.fieldDefinitions == null ? 0 : this.fieldDefinitions.Count) +
						(this.PredicateDefinition != null && !string.IsNullOrEmpty(this.PredicateDefinition.Select) ? 1 : 0);
			}
		}

		#endregion [+] cвойства

		#region методы

		/// <summary>
		/// Сравнение объектов
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public override bool Equals(object obj) {
			return Equals(obj as FieldManager);
		}

		/// <summary>
		/// Сравнение объектов FieldManager
		/// </summary>
		/// <param name="other"></param>
		/// <returns></returns>
		public bool Equals(FieldManager other) {
			if (other == null || ((other.FieldDefinitions == null) != (this.FieldDefinitions == null)) ||
			   ((other.PredicateDefinition == null) != (this.PredicateDefinition == null)))
				return false;

			if (other.PredicateDefinition != null && this.PredicateDefinition != null) {
				if (!other.PredicateDefinition.Equals(this.PredicateDefinition))
					return false;
			}

			if (other.FieldDefinitions != null && this.FieldDefinitions != null) {
				if (!other.FieldDefinitions.OrderBy(o => o).SequenceEqual(this.FieldDefinitions.OrderBy(o => o)))
					return false;
			}
			return true;
		}

		/// <summary>
		/// Получение Hash кода объекта FieldDefinitionCollection
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode() {
			return this.PredicateDefinition.GetHashCode() ^ this.FieldDefinitions.GetHashCode();
		}

		/// <summary>
		/// Метод добавления FieldDefinition в коллекцию
		/// </summary>
		/// <param name="fieldDefinition"></param>
		public void Add(FieldDefinition fieldDefinition) {
			if (fieldDefinition == null)
				return;

			if (this.fieldDefinitions == null)
				this.fieldDefinitions = new Collection<FieldDefinition>();

			if (!this.fieldDefinitions.Contains(fieldDefinition))
				this.fieldDefinitions.Add(fieldDefinition);
		}

		/// <summary>
		/// Метод удаления FieldDefinition из коллекции
		/// </summary>
		/// <param name="fieldDefinition"></param>
		public void Remove(FieldDefinition fieldDefinition) {
			if (fieldDefinition == null)
				return;

			if (this.fieldDefinitions != null)
				this.fieldDefinitions.Remove(fieldDefinition);
		}

		/// <summary>
		/// Метод очищения коллекции
		/// </summary>
		public void Clear() {
			if (this.fieldDefinitions != null)
				this.fieldDefinitions.Clear();
		}

		#endregion методы

		#region скрытые методы

		/// <summary>
		/// метод формирования строки выборки по коллекции полей
		/// </summary>
		/// <returns></returns>
		private SelectPredicate GeneratePredicate() {
			string predicateSelect = "";
			int indexPar = 0;
			Collection<object> predicateValues = new Collection<object>();
			StringBuilder select = new StringBuilder();
			string fieldPredicate = "";
			bool isMoreThanOne = false;

			if (this.PredicateDefinition != null && !string.IsNullOrEmpty(this.PredicateDefinition.Select)) {
				lock (this.PredicateDefinition) {
					predicateSelect = this.PredicateDefinition.Select;
					if (this.PredicateDefinition.Values != null) {
						foreach (object value in this.PredicateDefinition.Values) {
							predicateValues.Add(value);
						}
						indexPar += this.PredicateDefinition.Count;
					}
				}
			}
			if (this.FieldDefinitions != null) {
				lock (this.FieldDefinitions) {
					foreach (FieldDefinition item in this.FieldDefinitions) {
						if (!isMoreThanOne) {
							fieldPredicate = item.Predicate.Select;
							isMoreThanOne = true;
						}
						else {
							fieldPredicate = string.Concat(", ", item.Predicate.Select);
						}

						if (item.Values != null) {
							foreach (object value in item.Values) {
								predicateValues.Add(value);
							}
							indexPar += item.Values.Count;
						}
						select.Append(fieldPredicate);
					}
				}
			}

			if (string.IsNullOrEmpty(predicateSelect)) {
				predicateSelect = string.Concat("new (", select.ToString(), ")");
			}
			else {
				// Добавляем к получившейся выборке исходный по условию And
				fieldPredicate = select.ToString();
				if (!string.IsNullOrEmpty(fieldPredicate)) {
					predicateSelect = string.Concat("new (", predicateSelect, ", ", fieldPredicate, ")");
				}
			}
			return new SelectPredicate(predicateSelect, predicateValues);
		}

		#endregion скрытые методы
	}
}